#include <iostream>
#include <cstdio>
#include <cstring>
#include <cassert>
#include <ctype.h>
using namespace std;

#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define MAX_NUM 1024
#define MAX_STR 8

#define trimSpace(start) do{ \
        while(isspace(*start)) start++; \
    }while(0)

static int lastExit_Code;
static int lastSig;

enum REDIR
{
    NON_REDIR,
    INTPUT_REDIR,
    OUTPUT_REDIR,
    APPEND_REDIR
};

int redir_types = NON_REDIR;
char* redir_file = nullptr;  

void substring(char* argv[], char* buffer)
{
    int i = 0;
    argv[i++] = strtok(buffer," ");
    if(argv[0] != nullptr && strcmp(argv[0],"ls") == 0)
        argv[i++] = (char*)"--color=auto";
    while(argv[i++] = strtok(nullptr," "));
}

//检查是否存在重定向
void commandCheck(char* commands)
{
    assert(commands);
    char* start = commands;
    char* end = commands + strlen(commands);

    while(start < end)
    {
        if(*start == '<')
        {
            *start = 0;
            start++;
            redir_types = INTPUT_REDIR;
            trimSpace(start);
            redir_file = start;
        }
        else if(*start == '>')
        {
            *start = 0;
            start++;
            if(*start == '>')
            {
                redir_types = APPEND_REDIR;
                start++;
            }
            else
            {
                redir_types = OUTPUT_REDIR;
            }
            trimSpace(start);
            redir_file = start;
        }
        else
        {
            start++;
        }
    }
}


int main()
{
    //父进程派发子进程去完成指定命令
    while(1)
    {
        redir_file = nullptr;
        redir_types = NON_REDIR;
        char buffer[MAX_NUM];
        cout << "请输入指令> ";
        fgets(buffer,sizeof(buffer)-1,stdin);
        buffer[strlen(buffer)-1] = 0; // 'n' -> '\0'
        
        commandCheck(buffer); //检查重定向

        //切割子串
        char* argv[MAX_STR];
        substring(argv,buffer);

#ifdef _DEBUG_
    if(redir_file != nullptr)
        cout << redir_file << endl;

    // for(int i = 0; argv[i]; i++)
    // {
    //     printf("argv[%d] = %s\n", i,argv[i]);
    // }
#endif
        
        //内建命令
        if(argv[0] && strcmp(argv[0],"cd") == 0)
        {
            if(argv[1])     chdir(argv[1]);
            continue;
        }
        if(argv[0] && strcmp(argv[0],"echo") == 0)
        {
            if(argv[1] && strcmp(argv[1],"$?") == 0)
                cout << lastExit_Code << endl;
            else
                cout << argv[1] << endl;
            
            continue;
        }


        pid_t id = fork();
        if(id == 0)
        {
            //child
            //重定向
            //进程退出时，文件如果只被这个进程打开，那么文件也会被关闭
            switch(redir_types)
            {
            case INTPUT_REDIR:
                {
                    int fd = open(redir_file,O_RDONLY);
                    if(fd < 0){
                        perror("open");
                        exit(1);
                    }
                    dup2(fd,0);
                }
                break;
            case OUTPUT_REDIR:
            case APPEND_REDIR:
                {
                    umask(0);
                    int flag = O_WRONLY | O_CREAT;
                    if(redir_types == APPEND_REDIR) flag |= O_APPEND;
                    else    flag |= O_TRUNC;
                    int fd = open(redir_file,flag,0666);
                    if(fd < 0){
                        perror("open");
                        exit(1);
                    }
                    dup2(fd,1);
                }
                break;
            case NON_REDIR:
            break;
            default:
                cout << "bug?" << endl;
                break;
            }
            execvp(argv[0],argv);
            exit(1);
        }
        else 
        {
            //parent
            //等待子进程
            int status = 0;
            waitpid(id,&status,0);//阻塞等待
            lastExit_Code = ((status>>8)&0xff);
            lastSig = (status&0x7f);
        }
    }

    return 0;
}